<?php
/**
 * URI PATHINFO解析器
 * 本脚本已考虑magic_quotes_gpc不转义$_SERVER问题。可通过属性$addslashesOn进行设置。
 * 本脚本不考虑千奇百怪的URI，不考虑PATHINFO中变量重复定义导致覆盖问题，不提供过多的兼容性，需要作者严格遵循URI和PATHINFO规则。
 * URI的规范例子请见属性$methodID“解析方式”的注释。
 * 结果存储于属性$result中。
 * 
 * @author Horse Luke<horseluke@126.com>
 * @copyright Horse Luke, 2009
 * @license the Apache License, Version 2.0 (the "License"). {@link http://www.apache.org/licenses/LICENSE-2.0}
 * @version $Id: URI.php 117 2010-07-04 06:14:19Z horseluke@126.com $
 * @package Inter_PHP_Framework
 */

class Inter_Parser_URI
{
    /**
     * 解析方式。可选值有（以下假定分割符号为'/'）：
     * 0：关闭PATHINFO解析模式。此种方式下，直接读取$_GET数组
     * 1：PATHINFO兼容模式。适合于大部分服务器。此种情况下，将读取$_SERVER['QUERY_STRING']进行解析
     *     兼容方式的URI表现如下：http://www.demo.com/index.php?controllerName/actionName/tid/44/page/1
     * 2：PATHINFO模式A，显示当前的脚本文件名。此种情况下，将读取$_SERVER['PATH_INFO']或者$_SERVER['REQUEST_URI']和$_SERVER['SCRIPT_NAME']进行解析
     *     PATHINFO模式A的URI表现如下：http://www.demo.com/index.php/controllerName/actionName/tid/44/page/1
     * 3：PATHINFO模式B（URLRewrite模式），隐藏当前的脚本文件名。此种情况下，将读取$_SERVER['REQUEST_URI']和$_SERVER['SCRIPT_NAME']进行解析
     *     PATHINFO模式B的URI表现如下：http://www.demo.com/controllerName/actionName/tid/44/page/1
     * 
     * @var integer
     */
    public $methodID = 0;
    
    public $separator = '/';     //PATHINFO的分割符号
    
    public $addslashesOn = 1;     //是否输出addslashes处理的结果（无论magic_quotes_gpc是否开启）？1为是，0为否（将返回未转义的结果，无论magic_quotes_gpc是否开启）
    
    public $result = array();    //处理结果
    
    
    /**
     * 构造函数。
     *
     * @param integer $addslashesOn
     */
    public function __construct(){
    }
    
    /**
     * 解析URI并返回数组（调用该方法即可一次性解决）
     *
     * @return array 解析结果
     */
    public function parse(){
        $callFuncName = 'parseByMethod'.$this->methodID;
        return $this->$callFuncName();
    }
    
    /**
     * $this->methodID 为 0 时候的URI解析
     * 0：关闭PATHINFO解析模式。
     *
     * @return array 解析结果
     */
    public function parseByMethod0(){
        
        /*方法一：逻辑运算符
        是否需要人工对字符进行addslashes或者stripslashes处理？
        ini_get('magic_quotes_gpc')和$this->addslashesOn一致时，返回false，表明不需要人工处理，直接返回php给出的值。
        反之，则需要人工处理。
        */
        $stringProcess = ini_get('magic_quotes_gpc') xor $this->addslashesOn; 
        
        if( false === $stringProcess ){
            $this->result = $_GET ;
        }else{
            $this->result = (1 === $this->addslashesOn) ? $this->addslashesArray($_GET) : $this->stripslashesArray($_GET) ;
        }
        
        /*方法二：传统条件分支法
        switch (ini_get('magic_quotes_gpc')){
            case 0:
                if( 1 == $this->addslashesOn ){
                    $this->result = $this->addslashesArray($_GET) ;
                }else{
                    $this->result = $_GET ;
                }
                break;
            case 1:
                if( 1 == $this->addslashesOn ){
                    $this->result = $_GET ;
                }else{
                    $this->result = $this->stripslashesArray($_GET) ;
                }
                break;
            default:
                trigger_error("magic_quotes_gpc VALUE IS NOT CORRECT!", 256);
                break;
        }
        */

        return $this->result;
    }

    /**
     * $this->methodID 为 1 时候的URI解析
     * 1：PATHINFO兼容模式。适合于大部分服务器。此种情况下，将读取$_SERVER['QUERY_STRING']进行解析
     * @return array 解析结果
     */
    public function parseByMethod1(){
        if(!empty($_SERVER['QUERY_STRING'])){
            $this->parsePATHINFO( $_SERVER['QUERY_STRING'] );
        }
        return $this->result;
    }
    
    /**
     * $this->methodID 为 2 时候的URI解析
     * 2：PATHINFO模式A，显示当前的脚本文件名。
     * @return array 解析结果
     */
    public function parseByMethod2(){
        //Apache 2中设置AcceptPathInfo = On后将出现该变量。IIS一般有此变量。
        if(isset($_SERVER['PATH_INFO'])){
            $this->parsePATHINFO( $_SERVER['PATH_INFO'] );
        }else{
            //一般人们都是以include或者require的方式使用本文件，因此$_SERVER['SCRIPT_NAME']可能不会受到CGI方式影响（猜想的，还没验证），因此不用$_SERVER['PHP_SELF']
            $this->parsePATHINFO( str_replace($_SERVER['SCRIPT_NAME'],'',$_SERVER['REQUEST_URI']) );
        }
        return $this->result;
    }
    
    /**
     * $this->methodID 为 3 的URI解析
     * 3：PATHINFO模式B（URLRewrite模式），隐藏当前的脚本文件名。
     * @return array 解析结果
     */
    public function parseByMethod3(){
        $scriptName = end(explode('/',$_SERVER['SCRIPT_NAME']));
        $path =  str_replace($scriptName,'',$_SERVER['SCRIPT_NAME']);
        $pathinfo = str_replace($path,'',$_SERVER['REQUEST_URI']);
        $this->parsePATHINFO( $pathinfo );
        return $this->result;
    }
    
    
    /**
     * PATHINFO字符串解析。
     * PATHINFO字符串格式如下：/aaa/bbb/ccc/ddd，或者aaa/bbb/ccc/ddd。
     * 假如指定了$this->separator为其他分割符，比如，分割符为"-"，那么其字符串格式为/aaa-bbb-ccc-ddd，或者aaa-bbb-ccc-ddd
     * 允许字符串最前面或者最后面存在一个或者多个"/"。当中间连续出现//\\这类东西，则去掉。
     * 该方法依赖于$this->separator、$this->addslashesOn；结果存储于$this->result
     *
     * @param string $string 要解析的字符串
     */
    public function parsePATHINFO($string){
        //假如最前面或者最后面存在一个或者多个'/'，或者连续出现//\\这类东西，则去掉。
        $string = trim( urldecode($string) ,"\x20\x2F\x5C\x09\x0A\x0D\x00\x0B");
        $search = array( 1 => '/[\/\\\\]{2,}/');
        $replace = array( 1 => '/');
        $string = preg_replace($search,$replace,$string);
        
        $tmpArray = explode( $this->separator , $string );
        if( !empty( $tmpArray ) ){

            //请参考本类方法方法parseByMethod0的说明
            $stringProcess = ini_get('magic_quotes_gpc') xor $this->addslashesOn;

            //PATHINFO转化为数组 - 方法2：无法找寻出处，请原作者联系之
            foreach( $tmpArray as $key => $value ){
                if( 0 == ($key % 2) ){
                    if( false === $stringProcess ){
                        $keyTmp = $value ;
                    }else{
                        $keyTmp = (1 == $this->addslashesOn) ? addslashes($value) : stripslashes($value) ;
                    }
                    if( isset($tmpArray[$key+1]) ){
                        if( false === $stringProcess ){
                            $this->result[$keyTmp] = $tmpArray[$key+1];
                        }else{
                            $this->result[$keyTmp] = (1 == $this->addslashesOn) ? addslashes($tmpArray[$key+1]) : stripslashes($tmpArray[$key+1]);
                        }
                    }else{
                        $this->result[$keyTmp] = '';
                    }
                }
            }
            
        }
          
    }
    
    /**
     * addslashes处理数组专门版——对array的key都作处理。
     *
     * @param array $arrayBlock 要处理的数组
     * @return array 数组处理结果
     */
    public function addslashesArray ( $arrayBlock ){
        $tempArray = array();
        foreach ( $arrayBlock as $key => $value ){
            $key = addslashes($key);
            if(is_array($value)){
                $tempArray[$key] = $this->addslashesArray($value);
            }else{
                $tempArray[$key] = addslashes($value);
            }
        }
        return $tempArray;
    }
    
    /**
     * stripslashes处理数组专门版——对array的key都作处理。
     * PHP示例中所使用的array_map进行回调处理，经检验，对KEY处理无效
     *
     * @param array $arrayBlock 要处理的数组
     * @return array 数组处理结果
     */
    public function stripslashesArray ( $arrayBlock ){
        $tempArray = array();
        foreach ( $arrayBlock as $key => $value ){
            $key = stripslashes($key);
            if(is_array($value)){
                $tempArray[$key] = $this->stripslashesArray($value);
            }else{
                $tempArray[$key] = stripslashes($value);
            }
        }
        return $tempArray;
    }
    
}